package com.game.main {
	import com.game.ammo.Ammo;
	import com.game.ammo.AmmoFactory;
	import com.game.data.Data;
	import com.game.data.SelectedTowerObject;
	import com.game.events.GameEvent;
	import com.game.gui.DrawMap;
	import com.game.path.PathFinder;
	import com.game.pool.EnemyPool;
	import com.game.pool.TowerPool;
	import com.game.units.Enemy;
	import com.game.units.Explosion;
	import com.game.units.Tower;
	import com.game.utils.TowerType;
	import flash.display.Bitmap;
	import flash.display.BitmapData;
	import flash.display.Shape;
	import flash.display.Sprite;
	import flash.events.Event;
	import flash.geom.Point;
	import flash.geom.Rectangle;
	import flash.net.LocalConnection;
	import flash.utils.clearTimeout;
	import flash.utils.setTimeout;
	public class MainGraphics extends Sprite {
		private var data:Data;
		private var drawBasicMap:DrawMap;
		private var testPositionShape:Shape;
		private var enemyArray:Vector.<Enemy> = new Vector.<Enemy>();
		private var ammoArray:Vector.<Ammo> = new Vector.<Ammo>();
		private var towerArray:Vector.<Tower> = new Vector.<Tower>();
		private var explosionHolder:Vector.<Explosion> = new Vector.<Explosion>();
		private var postEffect:Bitmap;
		private var bulletUpperContainer:Bitmap
		//
		private var enemyContainer:Sprite = new Sprite();
		//
		private var towerContainer:Sprite = new Sprite();
		//
		private var enemyPool:EnemyPool = new EnemyPool();
		private var enemyWalkCounter:int = 0;
		//
		private var towerPool:TowerPool;
		private var explosionRect:Rectangle = new Rectangle(0, 0, Data.STEP, Data.STEP);
		private var explosionPoint:Point = new Point();
		private var timeoutId:uint = 0;
		public function MainGraphics() {
			data = Data.getInstance();
			//var stats:Stats=new Stats();
			//addChild(stats);
			//stats.x=data.myLevel[0].length * DataHolder.STEP;
			towerPool = new TowerPool(data.myLevel[0].length * data.myLevel.length);
			drawBasicMap = new DrawMap(Data.STEP, data.myLevel, data.iniTilePoint, data.finTilePoint);
			addChild(new Bitmap(drawBasicMap));
			//
			this.addChild(towerContainer);
			//
			this.addChild(enemyContainer);
			//
			//
			postEffect = new Bitmap(new BitmapData(data.myLevel[0].length * Data.STEP, data.myLevel.length * Data.STEP, true, 0x0000ff00));
			bulletUpperContainer = new Bitmap(postEffect.bitmapData.clone());
			this.addChild(bulletUpperContainer);
			addChild(postEffect);
			//
			testPositionShape = new Shape();
			addChild(testPositionShape);
			data.addEventListener(GameEvent.GET_TOWER_AT_POINT, locateTower);
			data.addEventListener(GameEvent.TOWER_REMOVE, locateTowerToRemove);
			data.addEventListener(GameEvent.TOWER_UPGRADE, upgradeTower);
			data.addEventListener(GameEvent.TOWER_CREATE, generateTower);
		}
		public function stopGame():void {
			var i:int = 0;
			while (enemyArray.length) {
				this.removeEnemy(enemyArray[0]);
			}
			while (ammoArray.length) {
				this.removeAmmo(ammoArray[0]);
			}
			while (this.explosionHolder.length) {
				this.removeExplosion(0);
			}
			while (this.towerArray.length) {
				
				this.removeTower(0);
			}
			clearTimeout(timeoutId);
			data.waveCounter = 0;
			data.myPoints = 80;
			data.goneFreeCounter = 20;
			data.myLevel=Data.copyLevel();
			clearPostEffectContainer();
			clearBulletUpperContainer();
			stop();
		}
		public function stop():void {
			this.removeEventListener(Event.ENTER_FRAME, onTick);
		}
		public function start():void {
			this.addEventListener(Event.ENTER_FRAME, onTick);
		}
		public function initiateGame():void {
			//initial wave create
			waveDelay();
			// game loop
			start();
		}
		private function generateTower(e:GameEvent):void {
			buildTower();
			repositionWalkers();
		}
		private function waveDelay():void {
			//trace("delay")
			timeoutId = setTimeout(addWave, 4000);
		}
		private function addWave():void {
			if (!data.currentWave) {
				return;
			}
			for (var i:int = 0; i < data.currentWave.length; i++) {
				addEnemy(data.currentWave[i], i);
			}
			enemyWalkCounter = 0;
		}
		private function checkPath(level:Array, _initPoint:Point, _finPoint:Point, draw:Boolean = true):Boolean {
			var path:Array = PathFinder.find(_initPoint, _finPoint, level);
			if (path) {
				return false;
			} else {
				return true;
			}
		}
		private function onTick(event:Event):void {
			if (Data.getInstance().isPaused) {
				return;
			}
			var i:int = 0;
			while (i < enemyArray.length) {
				//trace();
				if (enemyArray[i].readyForRemoval) {
					removeEnemy(enemyArray[i]);
				} else {
					enemyArray[i].move();
					i++;
				}
			}
			for each (var twr:Tower in towerArray) {
				var delta:Number = 0;
				var dlt:Number;
				var suggestiveDistance:Number;
				var ang:Number;
				var inRange:Boolean = false;
				for each (var en_twr:Enemy in enemyArray) {
					delta = (twr.x - en_twr.x) * (twr.x - en_twr.x) + (twr.y - en_twr.y) * (twr.y - en_twr.y);
					if (delta < (twr.range * Data.STEP) * (twr.range * Data.STEP) && en_twr.visible) {
						ang = Math.atan2(twr.y - en_twr.y, twr.x - en_twr.x); //
						dlt = twr.range * Data.STEP;
						suggestiveDistance = delta;
						inRange = true;
						break;
					}
				}
				if (inRange) {
					twr.turn(ang);
					if (twr.shoot) {
						buildAmmo(twr.x, twr.y, ang, dlt, twr.type, twr.upgrades, suggestiveDistance);
					}
				} else {
					twr.reset();
				}
			}
			i = 0;
			if (ammoArray.length > 0) {
				clearBulletUpperContainer();
				
			}
			while (i < ammoArray.length) {
				if (ammoArray[i].readyForRemoval) {
					removeAmmo(ammoArray[i]);
				} else {
					ammoArray[i].move();
					bulletUpperContainer.bitmapData.copyPixels(Data.getInstance().ammoAnimArray[ammoArray[i].type.ordinal], ammoArray[i].rect, ammoArray[i].point, null, null, true);
					i++;
				}
			}
			for each (var ammo:Ammo in ammoArray) {
				for each (var en_ammo:Enemy in enemyArray) {
					if (!ammo.readyForRemoval && ammo.isHit(en_ammo)) {
						ammo.hit = true;
						en_ammo.isHit(ammo.type, ammo.damage);
					}
				}
			}
			//explosion
			i = 0;
			if (explosionHolder.length > 0) {
				clearPostEffectContainer();
			}
			while (i < this.explosionHolder.length) {
				if (explosionHolder[i].dead) {
					removeExplosion(i);
				} else {
					explosionPoint.x = explosionHolder[i].x;
					explosionPoint.y = explosionHolder[i].y;
					postEffect.bitmapData.copyPixels(data.explosion[explosionHolder[i].step], explosionRect, explosionPoint, null, null, true);
					explosionHolder[i].step++;
					i++;
				}
			}
		}
		private function clearBulletUpperContainer():void {
			bulletUpperContainer.bitmapData.fillRect(bulletUpperContainer.bitmapData.rect, 0x0);
		}
		private function clearPostEffectContainer():void {
			postEffect.bitmapData.fillRect(postEffect.bitmapData.rect, 0x0);
		}
		private function removeExplosion(num:int):void {
			explosionHolder[num] = null;
			explosionHolder.splice(num, 1);
		}
		private function addEnemy(enemyData:Object, i:int):void {
			enemyArray.push(enemyPool.getObject());
			enemyContainer.addChild(enemyArray[enemyArray.length - 1]);
			enemyArray[enemyArray.length - 1].init(enemyData.type, enemyData.air, PathFinder.find(data.iniTilePoint, data.finTilePoint, data.myLevel, enemyData.air), enemyData.speed, enemyData.health, enemyData.reward, i * Data.ENEMY_RELEASE_DELAY);
		}
		private function removeEnemy(enemy:Enemy):void {
			data.myPoints += enemy.reward;
			var num:int = enemyArray.indexOf(enemy)
			if (enemy.reward == 0) {
				data.goneFreeCounter--;
			} else {
				createExplosion(enemy.x - Data.STEP / 2, enemy.y - Data.STEP / 2);
			}
			enemyContainer.removeChild(enemy);
			enemyPool.returnObject(enemy);
			enemyArray.splice(num, 1);
			if (enemyArray.length == 0) {
				data.waveCounter++;
				waveDelay();
				gc();
			}
		}
		private function gc():void {
			try {
				new LocalConnection().connect('foo');
				new LocalConnection().connect('foo');
			} catch (e:*) {
			}
		}
		private function createExplosion(x:int, y:int):void {
			var exp:Explosion = new Explosion();
			exp.x = x;
			exp.y = y;
			exp.length = data.explosion.length;
			explosionHolder.push(exp);
		}
		private function removeAmmo(ammo:Ammo):void {
			ammo.clear();
			var num:int = ammoArray.indexOf(ammo);
			ammoArray[num] = null;
			ammoArray.splice(num, 1);
		}
		private function buildAmmo(bx:int, by:int, ang:Number, dest:Number, type:TowerType, upgrades:int, suggestiveDistance:int):void {
			Data.getInstance().soundAdd = type.ordinal;
			var ammo:Ammo = AmmoFactory.getAmmo(type, bx, by, ang, dest, upgrades, suggestiveDistance);
			ammoArray.push(ammo);
		}
		private function buildTower():void {
			towerArray.push(towerPool.getObject());
			towerArray[towerArray.length - 1].init(data.towerData.x, data.towerData.y, data.towerData.type, data.weapons[data.towerData.type.ordinal].upgradeCost, data.weapons[data.towerData.type.ordinal].range, data.weapons[data.towerData.type.ordinal].cost);
			this.towerContainer.addChild(towerArray[towerArray.length - 1]);
		}
		private function locateTower(e:Event = null):void {
			for (var k:int = 0; k < this.towerArray.length; k++) {
				if (towerArray[k].posX == data.checkTowerAtPoint.x && towerArray[k].posY == data.checkTowerAtPoint.y) {
					data.selectedTowerData = new SelectedTowerObject(towerArray[k].type, towerArray[k].upgrades, k, towerArray[k].upgradeCost, towerArray[k].cost, towerArray[k].range);
					return;
				}
			}
		}
		private function repositionWalkers():void {
			for (var i:int = 0; i < enemyArray.length; i++) {
				if (!enemyArray[i].readyForRemoval && !Boolean(enemyArray[i].isAir)) {
					enemyArray[i].setPath(PathFinder.find(enemyArray[i].nextPoint, data.finTilePoint, data.myLevel))
				}
			}
		}
		private function locateTowerToRemove(e:Event):void {
			var num:int = data.towerIdToRemove;
			removeTower(num);
			repositionWalkers();
		}
		private function removeTower(num:int):void {
			towerContainer.removeChild(towerArray[num]);
			towerPool.returnObject(towerArray[num]);
			towerArray.splice(num, 1);
		}
		private function upgradeTower(e:Event):void {
			var num:int = data.towerIdToUpgrade;
			towerArray[num].upgrades++;
		}
	}
}